﻿using System.Collections.Generic;
using System.Windows.Forms;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Helper;
using SdmxMl.Multiplexor;

namespace SdmxMl.Manager
{
    /// <summary> Helper for Artefacts attached in GUI treeview </summary>
    public class ReferenceManager
    {
        #region Private C'tor

        private ReferenceManager()
        {
        }
        #endregion

        #region static members

        private static TreeView tv;
        private static ArtefactManager amProcessScheme;
        private static ArtefactManager amConceptScheme;
        private static ArtefactManager amCategoryScheme;
        private static ArtefactManager amCodelists;
        private static ArtefactManager amOrgScheme;
        private static ArtefactManager amMetadataStructureDef;
        private static MetadataSetManager amMetadataSet;
        public static ArtefactRefTyped ArTypedCopyInstance;
        public static string ArTypedCopyInstanceUrn;

        private static ArtefactManager amDsd;
        public static TreeSdmxManager TreeMgr { get; set; }
        public static PluginManager PlugInMgr { get; set; }

        public static TreeView TreeSdmx
        {
            get
            {
                return tv;
            }

            set
            {
                tv = value;
                if (tv != null)
                {
                    foreach (TreeNode node in tv.Nodes)
                    {
                        ArtefactManager am = node.Tag as ArtefactManager;
                        if (am != null)
                        {
                            if (am.ArtefactType == SdmxArtefactType.Concepts)
                                amConceptScheme = am;
                            if (am.ArtefactType == SdmxArtefactType.CodeLists)
                                amCodelists = am;
                            if (am.ArtefactType == SdmxArtefactType.KeyFamilies)
                                amDsd = am;
                            if (am.ArtefactType == SdmxArtefactType.CategorySchemes)
                                amCategoryScheme = am;
                            if (am.ArtefactType == SdmxArtefactType.OrganisationSchemes)
                                amOrgScheme = am;
                            if (am.ArtefactType == SdmxArtefactType.MetadataStructureDefinitions)
                                amMetadataStructureDef = am;
                            if (am.ArtefactType == SdmxArtefactType.Processes)
                                amProcessScheme = am;
                        }
                        else
                        {
                            MetadataSetManager m = node.Tag as MetadataSetManager;
                            if (m != null)
                                amMetadataSet = m;
                        }
                    }
                }
            }
        }
        #endregion

        #region Static Methods

        /// <summary>Locate requested manager attaced to 1st tree level </summary>
        /// <param name="aType">manager type</param>
        /// <returns>The manager instance or null</returns>
        public static ArtefactManager LocateArtefactManager(SdmxArtefactType aType)
        {
            ArtefactManager manager = null;
            foreach (TreeNode node in tv.Nodes)
            {
                ArtefactManager am = node.Tag as ArtefactManager;
                if (am != null && am.ArtefactType == aType)
                {
                    manager = am;
                    break;
                }
            }
            return manager;
       }

        /// <summary> Locate a specific concept from ConceptScheme reference and concept Id </summary>
        /// <param name="csInfo">Concept Scheme reference</param>
        /// <param name="conceptId">Concept id name </param>
        /// <returns>the Concept object or null if not found.</returns>
        /// <remarks>As with SDMX 2.0 we works only with concepts in concept Scheme: 
        /// Concept version and agency can be safely ignored.</remarks>
        public static ConceptType LocateConcept(ISdmxArtefactRef csInfo, string conceptId)
        {
            ConceptType c = null;
            ConceptSchemeType cs = LocateConceptScheme(csInfo);
            if (cs != null)
                c = cs.ConceptList.LocateItem(conceptId) as ConceptType;
            return c;
        }

        public static ConceptSchemeType LocateConceptScheme(ISdmxArtefactRef csInfo)
        {
            ConceptSchemeType cs = null;
            if (csInfo != null)
            {
                foreach (Artefact a in amConceptScheme.ArtefactList)
                    if (a.Id == csInfo.Id && a.AgencyId == csInfo.AgencyId && a.Version == csInfo.Version)
                    {
                        cs = a as ConceptSchemeType;
                        break;
                    }
            }

            return cs;
        }

        public static ProcessType LocateProcess(ISdmxArtefactRef csInfo)
        {
            ProcessType cs = null;
            if (csInfo != null)
            {
                foreach (Artefact a in amProcessScheme.ArtefactList)
                    if (a.Id == csInfo.Id && a.AgencyId == csInfo.AgencyId && a.Version == csInfo.Version)
                    {
                        cs = a as ProcessType;
                        break;
                    }
            }

            return cs;
        }

        public static MetadataStructureDefinitionType LocateMetadataStructureDefinition(ISdmxArtefactRef csInfo)
        {
            MetadataStructureDefinitionType cs = null;
            if (csInfo != null)
            {
                foreach (Artefact a in amMetadataStructureDef.ArtefactList)
                    if (a.Id == csInfo.Id && a.AgencyId == csInfo.AgencyId && a.Version == csInfo.Version)
                    {
                        cs = a as MetadataStructureDefinitionType;
                        break;
                    }
            }

            return cs;
        }

        public static ReportStructureType LocateReportStructure(ISdmxArtefactRef csInfo, string reportId)
        {
            ReportStructureType r = null;
            MetadataStructureDefinitionType cs = LocateMetadataStructureDefinition(csInfo);
            if (cs != null)
                r = cs.ReportStructureList.LocateItem(reportId);
            return r;
        }


        public static OrganisationType LocateAgency(string id)
        {
            OrganisationType agency = null;
            foreach (OrganisationSchemesType ost in amOrgScheme.ArtefactList)
                foreach (OrganisationType o in ost.Agencies)
                    if (o.Id == id)
                    {
                        agency = o;
                        break;
                    }

            return agency;
        }

        public static List<OrganisationType> GetAgencyList()
        {
            List<OrganisationType> agencyList = new List<OrganisationType>(100);
            if (amOrgScheme != null)
            {
                foreach (OrganisationSchemesType ost in amOrgScheme.ArtefactList)
                    foreach (OrganisationType o in ost.Agencies)
                        agencyList.Add(o);
            }

            return agencyList;
        }

        public static KeyFamilyType LocateDsd(ISdmxArtefactRef dsdInfo)
        {
            KeyFamilyType dsd = null;
            if (dsdInfo != null)
            {
                foreach (Artefact a in amDsd.ArtefactList)
                    if (a.Id == dsdInfo.Id && a.AgencyId == dsdInfo.AgencyId && a.Version == dsdInfo.Version)
                    {
                        dsd = a as KeyFamilyType;
                        break;
                    }
            }

            return dsd;
        }

        public static CodeListType LocateCodelist(ISdmxArtefactRef csInfo)
        {
            CodeListType cl = null;
            if (csInfo != null)
            {
                foreach (Artefact a in amCodelists.ArtefactList)
                    if (a.Id == csInfo.Id && a.AgencyId == csInfo.AgencyId && a.Version == csInfo.Version)
                    {
                        cl = a as CodeListType;
                        break;
                    }
            }

            return cl;
        }

        public static CategorySchemeType LocateCategoryScheme(ISdmxArtefactRef csInfo)
        {
            CategorySchemeType cl = null;
            if (csInfo != null)
            {
                foreach (Artefact a in amCategoryScheme.ArtefactList)
                    if (a.Id == csInfo.Id && a.AgencyId == csInfo.AgencyId && a.Version == csInfo.Version)
                    {
                        cl = a as CategorySchemeType;
                        break;
                    }
            }

            return cl;
        }


        public static ArtefactList GetArtefactList(SdmxArtefactType arType)
        {
            ArtefactList arList = null;
            if (tv != null)
            {
                foreach (TreeNode node in tv.Nodes)
                {
                    ArtefactManager am = node.Tag as ArtefactManager;
                    if (am != null)
                    {
                        if (am.ArtefactType == arType)
                        {
                            arList = am.ArtefactList;
                            break;
                        }
                    }
                }
            }
            return arList;
        }

        public static Artefact LocateArtefact(SdmxArtefactType arType, ISdmxArtefactRef artefactInfo)
        {
            ArtefactList artefactList = GetArtefactList(arType);
            if (artefactList != null)
                return LocateArtefact(artefactList, artefactInfo);
            return null;
        }

        public static Artefact LocateArtefact(ArtefactList artefactList, ISdmxArtefactRef artefactInfo)
        {
            Artefact ar = null;
            if (artefactInfo != null && artefactList != null)
            {
                foreach (Artefact a in artefactList)
                    if (a.Id == artefactInfo.Id && a.AgencyId == artefactInfo.AgencyId && a.Version == artefactInfo.Version)
                    {
                        ar = a;
                        break;
                    }
            }

            return ar;
        }

        public static IHierarchyManage ListFromIdentifierComponent(IdentifierComponentType ict)
        {
            IHierarchyManage fhl = null;

            switch (ict.RepresentationScheme.SchemeType)
            {
                case RepresentationSchemeTypeType.Category:
                    CategorySchemeType rst = LocateCategoryScheme(ict.RepresentationScheme);
                    if (rst != null)
                        fhl = rst.CategoryList;
                    break;

                case RepresentationSchemeTypeType.Codelist:
                    CodeListType clt = LocateCodelist(ict.RepresentationScheme);
                    if (clt != null)
                        fhl = clt.CodeList;
                    break;

                case RepresentationSchemeTypeType.Concept:
                    ConceptSchemeType cst = LocateConceptScheme(ict.RepresentationScheme);
                    if (cst != null)
                        fhl = cst.ConceptList;
                    break;

            }

            return fhl;

        }

        public static SmManager GetContainerWithRegistryProxy(Artefact target)
        {
            SmManager sm = new SmManager();

            if (tv != null)
            {
                foreach (TreeNode node in tv.Nodes)
                {
                    ArtefactManager am = node.Tag as ArtefactManager;
                    if (am != null && am.ArtefactType != SdmxArtefactType.DataSet &&
                        am.ArtefactType != SdmxArtefactType.MetadataSet)
                    {
                        foreach (Artefact a in am.ArtefactList)
                        {
                            if (a != target && Isfilled(a))
                            {
                                // Try to obtain from Registry
                                // If already there, set external
                                if (Mux.Instance.ExistArtefact(am.ArtefactType.ToString(), a.FullIdent))
                                    a.IsExternalReference = true;
                            }

                            sm.PutArtefact(am.ArtefactType, a);
                        }
                    }
                }

            }
            return sm;
        }
        public static void RefreshSmWithContainerImage(SmManager sm)
        {
            if (tv != null)
            {
                foreach (TreeNode node in tv.Nodes)
                {
                    ArtefactManager am = node.Tag as ArtefactManager;
                    if (am != null && am.ArtefactType != SdmxArtefactType.DataSet)
                    {
                        foreach (Artefact a in am.ArtefactList)
                        {
                            Artefact artefactOld = null;
                            // refresh with in tree memory
                            if ((artefactOld = sm.GetArtefact(am.ArtefactType, a)) != null)
                                sm.GetArtefactList(am.ArtefactType).Remove(artefactOld);
                            sm.PutArtefact(am.ArtefactType, a);
                        }
                    }
                }
            }
        }

        public static SmManager GetSmContainerImage()
        {
            SmManager sm = new SmManager();

            if (tv != null)
            {
                foreach (TreeNode node in tv.Nodes)
                {
                    ArtefactManager am = node.Tag as ArtefactManager;
                    if (am != null && am.ArtefactType != SdmxArtefactType.DataSet)
                    {
                        foreach (Artefact a in am.ArtefactList)
                            sm.PutArtefact(am.ArtefactType, a);
                    }
                }
            }
            return sm;
        }

        // Are Id and AgencyId specified
        public static bool Isfilled (Artefact a)
        {
            return string.IsNullOrEmpty(a.Id) == false &&
                string.IsNullOrEmpty(a.AgencyId) == false;
        }

        /// <summary>
        /// Check if artefact already in memory, If yes ask remove confirmation if required.
        /// Remove artefact if forced or confirmed.
        /// </summary>
        /// <param name="arType">Type of artefact to search for</param>
        /// <param name="aRef">reference for fullId</param>
        /// <param name="ask">if true ask confirmation before removing</param>
        /// <returns>True if artefact exists and user do not want to overwrite it</returns>
        public static bool RemoveExistingArtefact(SdmxArtefactType arType, ISdmxArtefactRef aRef, bool ask)
        {
            bool RefuseRemove = false;

            // If artefact already in memory ask for refresh
            Artefact aTarget = ReferenceManager.LocateArtefact(arType, aRef);
            if (aTarget != null)
            {
                TreeNode targetNode = null;
                TreeView tv = TreeSdmx;
                if (tv != null)
                {
                    TreeHelper.LocateObject(tv, aTarget, ref targetNode);
                    if (targetNode != null)
                    {
                        bool remove = ask == false ||
                            MessageBox.Show("Artefact is already in memory, refresh it with new Version ?", "Overwrite", 
                            MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes;
                        if (remove)
                        {
                            ArtefactManager arm = targetNode.Parent.Tag as ArtefactManager;
                            if (arm != null)
                            {
                                targetNode.Parent.Nodes.Remove(targetNode);
                                arm.ArtefactList.Remove(aTarget);
                            }
                        }
                        else
                            RefuseRemove = true;
                    }
                }
            }

            return RefuseRemove;
        }
        #endregion
    }
}
